﻿
/// <reference path="_namespace.js" />
/// <reference path="AjaxContainer.js" />


/*****************************************************************************
Ajax library class definition. Kevin Robinson 2006.
Class: ajax.components.DataGrid
/*****************************************************************************/

ajax.lang.addNamespace("ajax.components");

//-----------------------------------------------------------------------------
ajax.components.DataGrid = function(doc, parentNode, layerName, w, h, active)
{
    ajax.components.DataGrid.superConstructor.call(this, doc, parentNode, layerName, w, h);
    
    
    this.layerStyle.overflow    = "auto";    
    this.selectedRow        = null;        
    
    this.active             = typeof(active) != "boolean" ? false : active ;           
    this.htmlTable          = null;    
    this.htmlTableBody      = null;
    this.htmlTableHeaderRow = null;
    this.htmlTableRows      = [];
    
    this.dataSource         = null; // Array data source     
    this.formatting         = {formatArray: [], widthArray: []};
    this.ignoreColumns      = [];
    
    // Animation Colours
    this.animationStartColor    = ajax.lang.safeNew("ajax.drawing.Colour", 255, 255, 0);
    this.animationEndColor      = ajax.lang.safeNew("ajax.drawing.Colour", 0, 0, 100);
    
    // Public events
    this.getEvent(this.window, "onDataBoundHeaderRow");
    this.getEvent(this.window, "onDataBoundRow");
    this.getEvent(this.window, "onDataBoundHeaderCell");
    this.getEvent(this.window, "onDataBoundCell");
    this.getEvent(this.window, "onDataBound");
    this.getEvent(this.window, "onRowSelected");
    this.getEvent(this.window, "onRowDblClick");
    this.getEvent(this.window, "onRowUnSelected");
    this.getEvent(this.window, "onActivated");
    this.getEvent(this.window, "onDeActivated");
    
    
    // Freeze dry bound references to our Callbacks, so we can add or remove the handlers
    this.onRowMouseoverCallbackHandler  = this.onRowMouseoverCallback.bind(this);
    this.onRowMouseoutCallbackHandler   = this.onRowMouseoutCallback.bind(this);
    this.onRowClickCallbackHandler      = this.onRowClickCallback.bind(this);
    this.onRowDblClickCallbackHandler   = this.onRowDblClickCallback.bind(this);
    this.onDataTableUpdateHandler       = this.onDataTableUpdate.bind(this);
    
    // When each row is bound, add the dataRow as a property of the row
    this.getEvent(this.window, "onDataBoundRow").addListener(function(e, router)
        {
            e.tableRow.dataRow = e.dataRow;            
        }
    );
        
    
    // When the data is bound, set whether it is active or not
    this.getEvent(this.window, "onDataBound").addListener(function(e)
        {   
            this.setActive(this.active);
        }
    );
    
    
    // Styles
    this.htmlTableStyle = 
    {        
        backgroundColor: "#000066",
        color:           "Yellow"
    };       
    this.htmlTableHeaderRowStyle = 
    {
        fontWeight: "Bold",
        color: "Black",
        backgroundColor: "ButtonFace",
        fontSize: "24px"
    }
    this.rowStyle =
    {
        backgroundColor: "",
        color:  ""        
    };
    this.rowStyleSelected = 
    {
        backgroundColor: "Yellow",  
        color: "Black"
    }; 
    this.rowStyleMouseover = 
    {
        backgroundColor: "Blue"
    };
    this.rowStyleMouseout = 
    {
        backgroundColor: ""
    };
}
//-----------------------------------------------------------------------------
ajax.lang.extend("ajax.components.DataGrid", "ajax.components.AjaxContainer");
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Event Handler to load data from a DataTable's UpdateEvent
ajax.components.DataGrid.prototype.onDataTableUpdate = function(newData, router)
{        
    // Get the dataTable we wish to load data from 
    var dataTable = router.sender;
    
    // If it's not yet loaded, call getData on it.
    if (!dataTable.loaded)
    {
        dataTable.getData();
    }
    else
    {                                
        // If we are already loaded and there is only one row, try to update just that one row.
        if (this.htmlTable && this.htmlTableRows.length > 0 && newData && newData.length < 2)
        {
            // What if there is no new data?
            if (newData.length < 1)
                return;                
            
            var newDataRow      = newData[0];
            
            // Get current Table row (if exists)
            var tableRow = this.getTableRowByPrimaryKey(dataTable.primaryKey, newDataRow[dataTable.primaryKey]);
            if (tableRow)
            {
                // Update or delete
                if (newDataRow[dataTable.deletedRowMarker]) // Definately deleted
                {
                    // Animation here?
                    this.unSelectRows();
                    this.removeNode(tableRow);
                }
                else                                        // Definately updated
                {
                    this.dataBindRow(tableRow, newDataRow);
                    this.unSelectRows();
                    this.animate(tableRow);
                }
            }
            else                                            // Insert?
            {
                var newTableRow = this.createTableRow();        // Create new row
                this.setActiveRow(newTableRow, this.active);    // As it's new - it needs to be set active or not
                this.dataBindRow(newTableRow, newDataRow);      // Bind new data to row                        
                this.unSelectRows();                            // Ensure nothing is selected
                this.animate(newTableRow);                      // Animate row                                           
            }            
        }
        else // Total Data Bind.
        {            
            this.dataBind(dataTable.data);  // Rebind all data to our table 
            this.animate();                 // Animate the whole table.
        }       
    }
}

//-----------------------------------------------------------------------------
ajax.components.DataGrid.prototype.getTableRowByPrimaryKey = function(keyName, keyValue)
{
    if (!this.htmlTableRows)
        return null;
        
    for (var i = 0; i < this.htmlTableRows.length; i++)
    {
        // Grab the dataRow
        var tableRow = this.htmlTableRows[i];
        dataRow = tableRow.dataRow;
        if (dataRow[keyName] == keyValue)   // If it matches..
            return tableRow;                // Return it
    }
}

//-----------------------------------------------------------------------------
ajax.components.DataGrid.prototype.animate = function(ele)
{
    // Animates from animationStartColor to animationEndColor    
    var transform = ajax.lang.safeNew("ajax.effects.ColourTransform", this.doc,
        this.animationStartColor, this.animationEndColor, 15, 20);
    if (ele) 
        transform.affectElement(ele); // Transform ele passed
    else
    {        
        // transform all rows
        for (var i = 0; i < this.htmlTableRows.length; i++)
            transform.affectElement(this.htmlTableRows[i]);
    }
    transform.start();
}

//-----------------------------------------------------------------------------
ajax.components.DataGrid.prototype.getActive = function()
{
    return this.active;
}

//-----------------------------------------------------------------------------
ajax.components.DataGrid.prototype.setActive = function(val)
{
    // Set the hand cursor for each row
    if (this.htmlTableRows)
    {
        for (var i = 0; i < this.htmlTableRows.length; i++)
        {
            var row = this.htmlTableRows[i];
            this.setActiveRow(row, val);            
        }
    }
    
    this.active = val;

    // Fire event
    if (this.active)
        this.onActivated.call(this);
    else
        this.onDeActivated.call(this);
}

//-----------------------------------------------------------------------------
ajax.components.DataGrid.prototype.setActiveRow = function(row, val)
{            
    if (val)
    {                            
        // Add mouseover effects
        ajax.lang.getEvent(this.window, row, "onmouseover").addListener(this.onRowMouseoverCallbackHandler);
        ajax.lang.getEvent(this.window, row, "onmouseout").addListener(this.onRowMouseoutCallbackHandler);
        // Add Click Handler
        ajax.lang.getEvent(this.window, row, "onclick").addListener(this.onRowClickCallbackHandler);
        ajax.lang.getEvent(this.window, row, "ondblclick").addListener(this.onRowDblClickCallbackHandler);
        
        row.style.cursor = "hand";
    }
    else
    {                
        // Remove mouseover effects
        ajax.lang.getEvent(this.window, row, "onmouseover").removeListener(this.onRowMouseoverCallbackHandler);
        ajax.lang.getEvent(this.window, row, "onmouseout").removeListener(this.onRowMouseoutCallbackHandler);
        // Add Click Handler
        ajax.lang.getEvent(this.window, row, "onclick").removeListener(this.onRowClickCallbackHandler);
        ajax.lang.getEvent(this.window, row, "ondblclick").removeListener(this.onRowDblClickCallbackHandler);
        
        row.style.cursor = "default";
    }
}

//-----------------------------------------------------------------------------
ajax.components.DataGrid.prototype.selectRow = function(row)
{
    this.unSelectRows();        
    this.selectedRow            = row; 
    this.copyProperties(row.style, this.rowStyleSelected, false);    
    // Fire event
    this.onRowSelected.call(this, row);
}    

//-----------------------------------------------------------------------------
ajax.components.DataGrid.prototype.unSelectRows = function()
{
    var row = this.selectedRow;
    if (row)
    {
        this.copyProperties(row.style, this.rowStyle, false);        
        this.selectedRow = null;
        
        // Fire event        
        this.onRowUnSelected.call(this, row);
    }
}

//-----------------------------------------------------------------------------
ajax.components.DataGrid.prototype.onRowMouseoverCallback = function(e, router)
{
    var row = router.sender;
    if (row != this.selectedRow)
        this.copyProperties(row.style, this.rowStyleMouseover, false);        
}

//-----------------------------------------------------------------------------
ajax.components.DataGrid.prototype.onRowMouseoutCallback = function(e, router)
{
    var row = router.sender;
    if (row != this.selectedRow)
        this.copyProperties(row.style, this.rowStyleMouseout, false);        
}

//-----------------------------------------------------------------------------
ajax.components.DataGrid.prototype.onRowClickCallback = function(e, router)
{           
    var row = router.sender;
    if (this.active)
    {
        this.unSelectRows();
        this.selectRow(row);
    }
}

//-----------------------------------------------------------------------------
ajax.components.DataGrid.prototype.onRowDblClickCallback = function(e, router)
{
    this.onRowClickCallback(e, router); // Ensure click is also registered.    
    var row = router.sender;
    this.onRowDblClick.call(this, row);
}


//-----------------------------------------------------------------------------
// dataBind. Creates a table based on the dataSource.
ajax.components.DataGrid.prototype.dataBind = function(rows)
{    
    this.dataSource = rows || this.dataSource;
    if (!this.dataSource || this.dataSource.length < 1)
    {
        ajax.debug.Output.writeLn(this + ": Nothing to bind data to.");
        this.dataSource = [];
    }    
  
    // We have valid data...    
    this.formatting             = this.formatting || {};
    this.formatting.widthArray  = this.formatting.widthArray || [];
    this.formatting.formatArray = this.formatting.formatArray || [];
    
    // Remove any previous table
    this.removeNode(this.htmlTable);
    
    this.htmlTable = this.doc.createElement("table"); 
    
    // Set table width
    this.htmlTable.style.width = (this.units.size.width == "px") ?
        Math.max(this.width - this.getElementScrollbarWidth(this.layer), 0) + this.units.size.width
        :
        this.width + this.units.size.width;
    
    ajax.lang.getEvent(this.window, this.htmlTable, "onselectstart").returnFalse = false;
    
    this.copyProperties(this.htmlTable.style, this.htmlTableStyle, true, false);  // Apply styling  
      
              
    this.htmlTableBody = this.doc.createElement("tbody");
    
    // Create Header
    this.htmlTableHeaderRow = this.doc.createElement("tr");
    this.copyProperties(this.htmlTableHeaderRow.style, this.htmlTableHeaderRowStyle, true, false);
    
        
    
    var colIndex = 0;
    for (var name in this.dataSource[0])
    {
        // Ignore this column?
        if (this.ignoreColumns.contains(name))
            continue;
            
        if (typeof(name) != Function)
        {
            var col = this.doc.createElement("td");
            // Set custom Width
            col.style.width = this.formatting.widthArray[colIndex] ? this.formatting.widthArray[colIndex] : "";
            col.innerHTML = name;
            this.htmlTableHeaderRow.appendChild(col);
            
            // Fire event for item
            this.onDataBoundHeaderCell.call(this, ajax.lang.safeNew
                (
                    "ajax.components.DataGrid.GridItemEventArgs",
                    null, this, this.dataSource[0], name, this.htmlTableHeaderRow, col
                )
            );
            colIndex++;
        }
    }       
    this.htmlTableBody.appendChild(this.htmlTableHeaderRow);
    
    // Fire event for HeaderRow
    this.onDataBoundHeaderRow.call(this, ajax.lang.safeNew
        (
            "ajax.components.DataGrid.GridRowEventArgs",            
            null, this, this.dataSource[0], this.htmlTableHeaderRow
        )
    );
    
    // Reset our rows
    this.htmlTableRows      = [];
    
    // Create Data rows
    for (var i = 0; i < this.dataSource.length; i++)
    {
        var row         = this.dataSource[i];       // Get Data row
        var tableRow    = this.createTableRow();    // Create Table Row                       
        this.dataBindRow(tableRow, row);            // Bind data to table row            
           
    }        
    // Append body to table
    this.htmlTable.appendChild(this.htmlTableBody);
    
    // Append table to layer
    this.layer.appendChild(this.htmlTable); 
             
    // Fire OnDataBound
    this.onDataBound.call(this, ajax.lang.safeNew("ajax.events.DataEventArgs", null, this.dataSource));
                    
                
    // Return the htmlTable
    return this.htmlTable;
}

//-----------------------------------------------------------------------------
ajax.components.DataGrid.prototype.createTableRow = function()
{
    // Call only if TBODY is created. This will create a row, add it to the htmlTableRows array, add it to TBODY and return it.
    var index = this.htmlTableRows.length;
    this.htmlTableRows[index] = this.doc.createElement("tr");
    this.htmlTableBody.appendChild(this.htmlTableRows[index]);
    return this.htmlTableRows[index];
}

//-----------------------------------------------------------------------------
ajax.components.DataGrid.prototype.dataBindRow = function(tableRow, dataRow)
{
    // Clear any current cells in this row????? Cell Handlers??
    for (var i = tableRow.childNodes.length; i >= 0; i--)
        this.removeNode(tableRow.childNodes[i]);
        
    colIndex = 0; // Reset this index
    for (var ele in dataRow)
    {
        // Ignore this?
        if (this.ignoreColumns.contains(ele))
            continue;
            
        if (typeof(ele) != Function)
        {
            var td = this.doc.createElement("td"); 
            
            // Set custom Formatting, using the function pointer supplied
            td.innerHTML = this.formatting.formatArray[colIndex] ? this.formatting.formatArray[colIndex](dataRow[ele]) :  dataRow[ele];
            td.innerHTML += "&nbsp;";
            
            tableRow.appendChild(td);
            
            
            //Fire Event for item (if there are any listeners - slight optimization)          
            this.onDataBoundCell.call(this, ajax.lang.safeNew
                (
                    "ajax.components.DataGrid.GridItemEventArgs", 
                    null, this, dataRow, ele, tableRow, td
                )
            );          
                          
            colIndex++;
        }
    }   
    
    //Fire Event for Row
    this.onDataBoundRow.call(this, ajax.lang.safeNew
        (
            "ajax.components.DataGrid.GridRowEventArgs",
            null, this, dataRow, tableRow
        )
    );
}

//-----------------------------------------------------------------------------
ajax.components.DataGrid.prototype.setSize = function(x, y)
{
    // call base
    ajax.components.DataGrid.superClass.setSize.apply(this, arguments);
    
    if (this.htmlTable)
    {
        // If at Pixel size - never let it encroach the scrollbar.
        this.htmlTable.style.width = (this.units.size.width == "px") ?
            Math.max(x - this.getElementScrollbarWidth(this.layer), 0) + this.units.size.width
            :
            x + this.units.size.width;
    }
}

//-----------------------------------------------------------------------------
ajax.components.DataGrid.prototype.toString = function()
{
    return "ajax.components.DataGrid";
}

//-----------------------------------------------------------------------------
ajax.components.DataGrid.cast = function(obj)
{///<returns type="ajax.components.DataGrid"/>
    return obj;
}



// Inner Classes
//-----------------------------------------------------------------------------

ajax.components.DataGrid.GridRowEventArgs = function(e, source, dataRow, tableRow)
{
    ajax.components.DataGrid.GridRowEventArgs.superConstructor.call(this, e, source, dataRow);
    this.tableRow = tableRow;
}

ajax.lang.extend(ajax.components.DataGrid.GridRowEventArgs, "ajax.events.DataRowEventArgs");
//-----------------------------------------------------------------------------
ajax.components.DataGrid.GridRowEventArgs.cast = function(obj)
{///<returns type="ajax.components.DataGrid.GridRowEventArgs"/>
    return obj;
}
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
ajax.components.DataGrid.GridItemEventArgs = function(e, source, dataRow, colName, tableRow, tableCell)
{
    ajax.components.DataGrid.GridItemEventArgs.superConstructor.call(this, e, source, dataRow, colName);
    this.tableRow = tableRow;
    this.tableCell = tableCell;
}

ajax.lang.extend(ajax.components.DataGrid.GridItemEventArgs, "ajax.events.DataItemEventArgs");
//-----------------------------------------------------------------------------
ajax.components.DataGrid.GridItemEventArgs.cast = function(obj)
{///<returns type="ajax.components.DataGrid.GridItemEventArgs"/>
    return obj;
}
//-----------------------------------------------------------------------------


//-----------------------------------------------------------------------------
ajax.components.DataGrid.Formatting = function(formatArray, widthArray)
{
    this.formatArray = formatArray || [];
    this.widthArray  = widthArray || [];
}
//-----------------------------------------------------------------------------
ajax.components.DataGrid.Formatting.cast = function(obj)
{///<returns type="ajax.components.DataGrid.Formatting"/>
    return obj;
}
//-----------------------------------------------------------------------------